#include "config.h"

#include "ms2wgen.h"

ecm_double	sesslen_alpha = 0.5;
ecm_double	sesslen_beta = 0.5;

ecm_double	interseek_mu = 2.2;
ecm_double	interseek_sigma = 2.84;

static LIST_HEAD(wg_sessions);

static msl_popularity_t	msl_popularity;
static msl_sesslen_t	msl_sesslen;
static msl_istime_t	msl_istime;

static ecm_uint32	sid_max = 1;

#ifdef PARANOIA

void
check_wg_sessions(void)
{
	struct list_head	*lp;
	ecm_uint32	tick = 0;
	wg_session_t	*s;
	wg_access_t	*access;

	list_for_each (lp, &wg_sessions) {
		s = list_entry(lp, wg_session_t, list);
		access = get_head_access(s);
		if (tick > 0) {
			ECMASSERT(access->tick_start >= tick);
		}
		tick = access->tick_start;
	}
}

#endif

static void
free_access(wg_access_t *access)
{
	list_del_init(&access->list);
	ecm_free(access);
}

static void
free_session(wg_session_t *session)
{
	ECMASSERT(list_empty(&session->accesses));
	list_del_init(&session->list);
	ecm_free(session);
}

static wg_session_t *
create_session(void)
{
	wg_session_t	*session;

	session = ecm_malloc(sizeof(wg_session_t));
	ECMASSERT(session != NULL);

	session->sid = sid_max++;
	session->mid = msl_popularity_get(msl_popularity);

	INIT_LIST_HEAD(&session->accesses);
	INIT_LIST_HEAD(&session->list);

	return session;
}

static void
create_access(wg_session_t *session, ecm_uint32 tick, ecm_uint32 t_start, ecm_uint32 t_next, ecm_uint32 pos)
{
	wg_access_t	*access;

	if (t_start > t_next)
		return;

	access = (wg_access_t *)ecm_malloc(sizeof(wg_access_t));
	access->tick_start = tick + t_start;
	access->tick_end = tick + t_next;
	access->startstamp = pos;
	access->endstamp = pos + (t_next - t_start);

	list_add_tail(&access->list, &session->accesses);
}

static void
build_access(wg_session_t *session, ecm_uint32 tick)
{
	ecm_uint32	sess_len;
	ecm_uint32	interseek_time;
	ecm_int32	seekdist;
	ecm_int32	pos_start;
	ecm_uint32	t_start;

	sess_len = msl_sesslen_get(msl_sesslen, media_length);

	pos_start = 0;
	for (t_start = 0; t_start < sess_len;) {
		if (msl_istime_get_seek(msl_istime, pos_start, media_length, &interseek_time, &seekdist) == 0)
			break;

		if (interseek_time == 0) {
			////TODO: it's a bug ?
			interseek_time = 1;
		}

		create_access(session, tick, t_start, t_start + interseek_time - 1, pos_start);
		
		t_start += interseek_time;
		pos_start += (interseek_time + seekdist);
		if (pos_start > (ecm_int32)media_length)
			pos_start = media_length;
		ECMASSERT(pos_start >= 0 && pos_start <= (ecm_int32)media_length);
	}

	create_access(session, tick, t_start, sess_len, pos_start);
}

void
add_wg_session(ecm_uint32 tick)
{
	wg_session_t	*session;

	session = create_session();

	build_access(session, tick);
	list_add(&session->list, &wg_sessions);
}

wg_access_t *
get_head_access(wg_session_t *session)
{
	wg_access_t	*wg_access;

	ECMASSERT(!list_empty(&session->accesses));

	wg_access = list_entry(session->accesses.next, wg_access_t, list);

	return wg_access;
}

wg_session_t *
get_wg_session(ecm_uint32 tick)
{
	wg_session_t	*session;
	wg_access_t	*access;

	if (list_empty(&wg_sessions))
		return NULL;

	session = list_entry(wg_sessions.next, wg_session_t, list);
	access = get_head_access(session);
	ECMASSERT(access != NULL);

	if (access->tick_start == tick)
		return session;
	return NULL;
}

static void
sort_wg_session(wg_session_t *session)
{
	wg_session_t	*session_list;
	wg_access_t	*access, *access_list;
	struct list_head	*lp;

	access = get_head_access(session);
	ECMASSERT(access != NULL);

	lp = session->list.next;
	list_del_init(&session->list);
	for (; lp != &wg_sessions; lp = lp->next) {
		session_list = list_entry(lp, wg_session_t, list);
		access_list = get_head_access(session_list);
		ECMASSERT(access_list != NULL);
		
		if (access_list->tick_start >= access->tick_start) {
			list_add_tail(&session->list, &session_list->list);
			return;
		}
	}
	list_add_tail(&session->list, &wg_sessions);
}

void
put_wg_session(wg_session_t *session)
{
	wg_access_t	*access;

	access = get_head_access(session);
	ECMASSERT(access != NULL);

	free_access(access);
	if (list_empty(&session->accesses)) {
		free_session(session);
	}
	else {
		sort_wg_session(session);
	}
}

ECMBOOL
init_wg_session(void)
{
	msl_popularity = msl_popularity_create(n_medias, skew, 0);
	msl_sesslen = msl_sesslen_create(MSL_SESSLEN_TYPE_BETA, 0);
	msl_sesslen_set_param(msl_sesslen, sesslen_alpha, sesslen_beta);
	msl_istime = msl_istime_create(MSL_ISTIME_TYPE_LOGNORMAL, 0);
	msl_istime_set_param(msl_istime, interseek_mu, interseek_sigma);
	
	return ECM_TRUE;
}
